package exam.driver.jdbc;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>JdbcManagerDao</p>
 * <br>
 * <p>
 * Class giao tiếp với database
 * </p>
 * @author PHONGTN
 * @version 18/4/2012(update)
 */

public class JdbcManagerDao<T> {
	// Đối tượng thực thi các truy vấn db
	private static SQLExecute sqlExecute;
	static {
		sqlExecute = new SQLExecute(new SQLConnection());
	}
	// Biến lưu kết quả của câu truy vấn select
	private List<T> result;
	/**
	 * select:JdbcManagerDao
	 * select dữ liệu từ database
	 * @param <T>
	 * @param <E>
	 * @param entityClass
	 * @param entityName
	 * @param statement
	 * @return JdbcManagerDao
	 */
	public <E> JdbcManagerDao<T> select(Class<T> entityClass, Class<E> entityName, String statement) {
		// biến lưu kết quả trả về
		result = new ArrayList<T>();
		if(sqlExecute.getSQLConnection().isClosed()) {
			sqlExecute.getSQLConnection().openConnect();
		}
		try {
			// tạo 1 truy vấn đến db
			sqlExecute.createStatementSql(statement);
			// Kết quả của câu lệnh truy vấn
			ResultSet rsSet = sqlExecute.select();
			// sao chép dữ liệu
			this.copyResultSetToClassEntity(result, rsSet, entityClass, entityName);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} finally {
			// Đóng kết nối
			sqlExecute.getSQLConnection().closeConnection();
		}
		return this;
	}
	/**
	 * copyResultSetToClassEntity: Thực hiện copy dữ liệu từ kết quả truy vấn vào result
	 * @param <E>
	 * @param result
	 * @param rsSet
	 * @param entity
	 * @param entityName
	 * @throws SQLException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 */
	private <E> void copyResultSetToClassEntity(List<T> result, ResultSet rsSet, Class<T> entity, Class<E> entityName)
			throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		ResultSetMetaData rsSetMeta = rsSet.getMetaData();
		// Class chứa định nghĩa trường table
		Field []fieldNames = entityName.getDeclaredFields();
		// xử lý kết quả
		while(rsSet.next()) {
			T item = entity.newInstance();
			Field fd = null;
			for(int i = 1; i <= rsSetMeta.getColumnCount(); i++) {
				// tên thuộc tính của item
				String fieldName = getFieldName(fieldNames, rsSetMeta.getColumnLabel(i));
				fd = item.getClass().getField(fieldName);
				// set giá trị tương ứng với thuộc tính của item
				fd.set(item, rsSet.getObject(fieldName));
			}
			result.add(item);	
		}
	}
	/**
	 * Phương thức trả về tên trường trong kết quả truy vấn
	 * @param fieldNames
	 * @param label
	 * @return label
	 */
	private static String getFieldName(Field []fieldNames, String label) {
		for(int i = 0; i < fieldNames.length; i++) {
			if (fieldNames[i].getName().equalsIgnoreCase(label)) {
				return label.toLowerCase();
			}
		}
		return null;
	}
	/**
	 * Kết quả trả về là 1 record
	 * @return T
	 */
	public T getSingleResult() {
		return (result.size() > 0)?result.get(0):null;
	}
	/**
	 * Kết quả trả về là 1 danh sách
	 * @return List<T>
	 */
	public List<T> getListResult() {
		return result;
	}

	/**
	 * delete:JdbcManagerDao
	 * Xóa các record có điều kiện là mảng khóa chính
	 * @param statement
	 * @param cond
	 * @return
	 */
	public List<Integer> delete(String statement, int[] arrayPk) {
		// Lưu lại những khóa chính xóa không thành công
		List<Integer> listResult = new ArrayList<Integer>(); 
		// Mở kết nối đến db
		if (sqlExecute.getSQLConnection().isClosed()) {
			sqlExecute.getSQLConnection().openConnect();
		}
		try {
			sqlExecute.createStatementSql(statement);
			for(int i = 0; i < arrayPk.length; i++) {
				sqlExecute.setValue(1, arrayPk[i]);
				if (sqlExecute.execute() == 0) {
					listResult.add(arrayPk[i]);
				}
			}
		} catch (SQLException e) {
			// Khôi phục dữ liệu
			// Khôi phục dữ liệu
			if (sqlExecute.getSQLConnection().isConnected()) {
				System.err.println("backup");
				sqlExecute.getSQLConnection().backup();
			}
			e.printStackTrace();
		} finally {
			// Đóng kết nối
			sqlExecute.getSQLConnection().closeConnection();
		}
		return listResult;
	}
	
	/**
	 * insert:JdbcManagerDao
	 * @param entity
	 * @param sqlFile
	 * @return
	 */
	public int insert(T entity, String sqlFile) {
		int rsInsert = 0;
		// Tạo kết nối đến db
		if (sqlExecute.getSQLConnection().isClosed()) {
			sqlExecute.getSQLConnection().openConnect();
		}
		sqlExecute.createStatementSql(sqlFile);
		Field[] fields = entity.getClass().getDeclaredFields();
		try {
			for(int i = 1; i < fields.length; i++) {
				sqlExecute.setValue(i, fields[i].get(entity));
			}
			rsInsert = sqlExecute.execute();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// Khôi phục dữ liệu
			if (sqlExecute.getSQLConnection().isConnected()) {
				sqlExecute.getSQLConnection().backup();
			}
			e.printStackTrace();
		} finally {
			sqlExecute.getSQLConnection().closeConnection();
		}
		return rsInsert;
	}
	
	/**
	 * update:JdbcManagerDao
	 * @param sqlFile
	 * @param params
	 * @return int
	 */
	public int update(String sqlFile, List<Object> params) {
		int rsUpdate = 0;
		// Tạo kết nối đến db
		if (sqlExecute.getSQLConnection().isClosed()) {
			sqlExecute.getSQLConnection().openConnect();
		}
		// Tạo truy vấn đến db
		String statement = ReaderSql.getStatementSql(sqlFile);
		sqlExecute.createStatementSql(statement);
		try {
			for(int i = 0; i < params.size(); i++) {
				sqlExecute.setValue(i + 1, params.get(i));
			}
			rsUpdate = sqlExecute.execute();
		} catch(SQLException e) {
			System.err.println("Error: JdbcManagerDao#update");
			e.printStackTrace();
			// Khôi phục dữ liệu
			if (sqlExecute.getSQLConnection().isConnected()) {
				System.err.println("Backup");
				sqlExecute.getSQLConnection().backup();
			}
		} finally {
			sqlExecute.getSQLConnection().closeConnection();
		}
		return rsUpdate;
	}
}
